package com.ymk.base.cmd;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.ymk.bo.ListParamType;
import com.ymk.base.exception.MallException;
import com.ymk.base.utlis.CacheUtil;
import com.ymk.core.code.redis.RedisCode;
import com.ymk.core.code.resp.RespCode;
import com.ymk.core.constants.SysConstants;
import com.ymk.core.constants.WxConstants;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Type;
import java.util.*;

@Component
public class SchedulerCmd {
    private final static Logger logger = LoggerFactory.getLogger(SchedulerCmd.class);
    private Map<String, ICommand> cmdMap = new HashMap<>();

    public CmdSign schedule(HttpServletRequest request, CmdSign sign) throws Exception {
        String cmdCode = sign.getCmdCode();
        if (StringUtils.isEmpty(sign.getCmdVersion())) {
            sign.setCmdVersion("1");
        }
        ICommand cmd = this.getCommand(sign);
        if (cmd != null) {
            CmdSign respSign = this.checkSession(request,sign, cmd);
            if (respSign == null) {
                TransactionStatus status = null;
                if (cmdCode.lastIndexOf("GET") == -1 && cmdCode.lastIndexOf("LIST") == -1 && cmd.openTx(sign)) {
                    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
                    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
                    status = transactionManager.getTransaction(def);
                }
                String lockKey = cmd.lockKey(sign);
                try {
                    if (lockKey != null) {
                        lockKey = "lock-" + sign.getCmdCode() + lockKey;
                        CacheUtil.getLock(lockKey, "true", 10000L, 6, 2000L);
                    }
                    CmdSign cmdSign = this.scheduleNext(request, sign, cmd);
                    if (status != null) {
                        transactionManager.commit(status);
                    }
                    if (lockKey != null) {
                        CacheUtil.releaseLock(lockKey, "true", RedisCode.DB_LOCK);
                    }
                    return cmdSign;
                } catch (Exception e) {
                    sign.endExe();
                    if (status != null) {
                        try {
                            transactionManager.rollback(status);
                        } catch (Exception e1) {
                        }
                    }
                    throw e;
                }
            } else {
                sign.endExe();
                return respSign;
            }
        } else {
            sign.endExe();
            throw new MallException(RespCode.CODE_ERROR, cmdCode + "命令未注册");
        }
    }

    private ICommand getCommand(CmdSign sign) {
        String cmdCode = sign.getCmdCode();
        if (StringUtils.isEmpty(sign.getCmdVersion())) {
            sign.setCmdVersion("1");
        }

        return this.cmdMap.get(cmdCode + sign.getCmdVersion());
    }

    private CmdSign checkSession(HttpServletRequest request,CmdSign sign, ICommand cmd) throws Exception {
        sign.setUserId(null);

        if (StringUtils.isNotBlank(sign.getToken())){
            String token = "";//this.redisTemplate.opsForValue().get(SysConstants.REDIS_KEY_JWT + sign.getToken());
            if (StringUtils.isNotBlank(token)){
                JSONObject jsonObject = JSON.parseObject(token);
                sign.setUserId(jsonObject.getInteger("id"));
            }
        }else if(Objects.nonNull(request)) {
            String openId = request.getHeader(WxConstants.X_WX_OPENID);
            if (StringUtils.isNotBlank(openId)) {
                sign.setIp(request.getHeader(WxConstants.X_ORIGINAL_FORWARDED_FOR));
                String token = "";//this.redisTemplate.opsForValue().get(SysConstants.REDIS_KEY_JWT + openId);
                if (StringUtils.isNotBlank(token)){
                    JSONObject jsonObject = JSON.parseObject(token);
                    sign.setUserId(jsonObject.getInteger("id"));
                    sign.setToken(jsonObject.getString("wxOpenId"));
                }
            }
        }

//        if (!cmd.anon(sign)) {
//            if (sign.getUserId() == null) {
//                sign.endExe();
//                throw new MallException(RespCode.CODE_SESSION_ERROR, "会话已过期");
//            }
//        }
        return null;
    }

    private CmdSign scheduleNext(HttpServletRequest request, CmdSign sign, ICommand cmd) throws Exception {
        CmdSign respSign;
        sign.startExe();
        respSign = cmd.verify(request, sign);
        if (respSign == null) {
            respSign = cmd.route(request, sign);
        }
        sign.endExe();
        if (cmd.nextCmd() != null && RespCode.SUCCESS.equals(respSign.getRespCode())) {
            ICommand nextCmd = this.cmdMap.get(cmd.nextCmd() + sign.getCmdVersion());
            return this.scheduleNext(request, sign, nextCmd);
        }
        return respSign;
    }

    public <T> T schedule(HttpServletRequest request, CmdSign sign, Class<T> clazz) throws Exception {
        CmdSign respSign = this.schedule(request, sign);
        if (RespCode.CODE_SUCCESS.equals(respSign.getRespCode())) {
            if ("{}".equals(respSign.getSource())) {
                return null;
            }
            Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
            return gson.fromJson(respSign.getSource(), clazz);
        } else {
            return null;
        }

    }

    public <T> List<T> schedule(HttpServletRequest request, CmdSign sign, Class<T> clazz, boolean returnArray) throws Exception {
        CmdSign respSign = this.schedule(request, sign);
        if (RespCode.CODE_SUCCESS.equals(respSign.getRespCode())) {
            if ("{}".equals(respSign.getSource())) {
                return new ArrayList<>();
            }
            Type type = new ListParamType(clazz);
            Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
            return gson.fromJson(respSign.getSource(), type);
        } else {
            return null;
        }
    }


    public void register(String code, ICommand cmd) throws Exception {
        if (this.cmdMap.containsKey(code)) {
            throw new Exception("重复注册cmd:" + code);
        }
        cmdMap.put(code, cmd);
    }

    @Autowired
    private DataSourceTransactionManager transactionManager;
    //@Autowired
   // private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private SchedulerFacade schedulerFacade;

}
